mod rpc_socket_channel;

use std::fs::OpenOptions;
use env_logger::Builder;
use log::{error, info, warn, LevelFilter};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio::time::{self, Duration};
use rpc::{RpcContext, RpcResult};
use rpc::bootstrap::BootstrapBuilder;
use rpc_derive::{router, rpc_router};
use rpc_server::server::RpcServerBuilder;

pub type Error = Box<dyn std::error::Error + Send + Sync>;

/// A specialized `Result` type for mini-redis operations.
///
/// This is defined as a convenience.

#[tokio::main]
async fn main3() -> RpcResult<()> {
    let listener = TcpListener::bind("127.0.0.1:8080").await?;

    loop {
        let (mut socket, _) = listener.accept().await?;

        tokio::spawn(async move {
            let mut buf = [0; 1024];

            // In a loop, read data from the socket and write the data back.
            loop {
                let n = match socket.read(&mut buf).await {
                    // socket closed
                    Ok(0) => return,
                    Ok(n) => n,
                    Err(e) => {
                        error!("failed to read from socket; err = {:?}", e);
                        return;
                    }
                };

                // Write the data back
                if let Err(e) = socket.write_all(&buf[0..n]).await {
                    error!("failed to write to socket; err = {:?}", e);
                    return;
                }
            }
        });
    }
}

#[tokio::main]
pub async fn main() -> RpcResult<()> {
    init_logger();

    let t : RpcResult<()>;
    log::info!("This is an example message.");

    let bootstrap = BootstrapBuilder::new()
        .router("/echo", router!(echo_test))
        .router("/world", router!(world))
        ;
    let _ = RpcServerBuilder::new(bootstrap, 8888)
        .build().start_server().await?;

    Ok(())
}

#[rpc_router]
pub fn echo_test(context: &mut RpcContext, data: i32) -> i32 {
    data + 100
}

#[rpc_router]
fn world(_context: &mut RpcContext, value: String) -> String {
    let mut result = value;
    result.push_str(" ok");
    result
}

// pub struct HelloRouterFactory;
// #[derive(Debug)]
// pub struct HelloRouter {
//     pub data: i32,
// }
// impl rpc::router::RouterFactory for HelloRouterFactory {
//     fn parse(&self, rpc_context: &dyn rpc::RpcContext, byte_muts: bytes::BytesMut) -> rpc::RpcResult<Box<dyn rpc::router::DoRouter>> {
//         Ok(Box::new(HelloRouter { data: rpc::decode::<i32>(rpc_context, byte_muts.freeze().to_vec())? })) } }
// impl rpc::router::DoRouter for HelloRouter {
//     fn execute(&mut self, mut rpc_context: &mut dyn rpc::RpcContext) -> rpc::RpcResult<Vec<u8>> {
//         let result = hello(rpc_context, self.data.clone());
//         rpc::encode(rpc_context, result)
//     }
// }

async fn accept(listener: &mut TcpListener) -> RpcResult<TcpStream> {
    let mut backoff = 1;

    // Try to accept a few times
    loop {
        // Perform the accept operation. If a socket is successfully
        // accepted, return it. Otherwise, save the error.
        match listener.accept().await {
            Ok((socket, _)) => return Ok(socket),
            Err(err) => {
                if backoff > 64 {
                    // Accept has failed too many times. Return the error.
                    return Err(err.into());
                }
            }
        }

        // Pause execution until the back off period elapses.
        time::sleep(Duration::from_secs(backoff)).await;

        // Double the back off
        backoff *= 2;
    }
}

fn init_logger() {
    let mut builder = Builder::new();
    builder.filter(None, LevelFilter::Info);


    // 使用 OpenOptions 打开或创建文件用于日志输出
    let file = OpenOptions::new()
        .create(true)
        .write(true)
        .truncate(true) // 如果设置为 false，则不会每次运行都清空文件，而是追加到文件末尾
        .open("logfile.log")
        .expect("Failed to create log file");

    builder.format_timestamp(None) // 可以选择是否格式化时间戳，这里不格式化时间戳
        .target(env_logger::Target::Pipe(Box::new(file)))
        // 设置目标为 Pipe，即管道，这里是之前打开的文件
        .init();

    info!("应用程序启动");
    warn!("这是一个警告信息");
    error!("这是一个错误信息");
}